﻿using ArtMath.Attribute;
using ArtMath.Component.Interface;
using ArtMath.Component.Point;
using ArtMath.Converter;
using ArtMath.Core.Data;
using ArtMath.Core.Helper;
using ArtMath.Data;
using ArtMath.PointCollection;
using ArtMath.Resource;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using ArtMath.CommonType;

namespace ArtMath.Component.Line
{
    /// <summary>
    /// 有向约束线段
    /// </summary>
    [Serializable]
    public class DirectedConstrainedLineSegment : LineSegmentBase, IDirectedAngle
    {
        PadStringX len;
        AngleStringX angle;
        double dirAngle;
        PropertyDisplayer dirAngleDisplayer;
        // 在修改长度和方向角时，由于需要移动其中一个点，所以需要此变量来指示本元件对该点的无条件允许移动
        bool permit, fixAngle, fixLength;
        public DirectedConstrainedLineSegment(SketchPad pad, string name, string dirAngle, string len, bool addToList = true) : base(pad, name, ToolTypeName.yxysxd, addToList)
        {
            DirectedAngleDisplayer = new PropertyDisplayer(this, "DirectedAngle");
            StrLength = new PadStringX(Pad, len ?? "1");
            fixLength = len != null;
            StrDirectedAngle = new AngleStringX(Pad, dirAngle ?? "0");
            fixAngle = dirAngle != null;
        }
        void Length_ExpressionSet(StringX sender, ExpressionSetEventArgs e)
        {
            if (FixedLength)
            {
                if (StrLength.Value < 0)
                {
                    Pad.ShowMessage(Resources.JoinLocalizedString("LSLength", "MustNonNegative"), MessageType.Error);
                    StrLength.SetExpression(e.OldExpression, false);
                }
                else
                {
                    Length = StrLength.Value;
                    if (tryRelocateAnotherPoint(DirectedAngle, Length))
                    {

                    }
                    else
                    {
                        Pad.ShowMessage(string.Format(Resources.Translate("FailModify"), OfTranslator("LSLength")), MessageType.Error);
                        StrLength.SetExpression(e.OldExpression, false);
                        Length = e.OldValue;
                    }
                }
            }
            else
            {
                Pad.ShowMessage(string.Format(Resources.Translate("CannotSetLength"), Name), MessageType.Exclamation);
                StrLength.SetExpression(e.OldExpression, false);
            }
            Pad.MoveObject_DoMouseUp(Point2.Movable ? Point2 : Point1);
        }
        void DirectedAngle_ExpressionSet(StringX sender, ExpressionSetEventArgs e)
        {
            if (FixedDirectedAngle)
            {
                DirectedAngle = StrDirectedAngle.RadValue;
                if (tryRelocateAnotherPoint(DirectedAngle, Length))
                {

                }
                else
                {
                    Pad.ShowMessage(string.Format(Resources.Translate("FailModify"), OfTranslator("DirectedAngle")), MessageType.Error);
                    StrDirectedAngle.SetExpression(e.OldExpression, false);
                    DirectedAngle = Pad.GetRad(e.OldValue);
                }
            }
            else
            {
                Pad.ShowMessage(string.Format(Resources.Translate("CannotSetDirectedAngle"), Name), MessageType.Exclamation);
                StrDirectedAngle.SetExpression(e.OldExpression, false);
            }
            Pad.MoveObject_DoMouseUp(Point2.Movable ? Point2 : Point1);
        }
        [MultilingualCategory(null, "Sketchpad", "Display"), MultilingualDisplayName(null, "DirectedAngle", "Displayer")]
        public PropertyDisplayer DirectedAngleDisplayer
        {
            get { return dirAngleDisplayer; }
            set { dirAngleDisplayer = value; }
        }
        [MultilingualCategory("Structure"), MultilingualDisplayName("LSLength")]
        public PadStringX StrLength
        {
            get { return len; }
            set
            {
                if (len is object)
                {
                    len.ParametersValueChanging -= Length_ParametersValueChanging;
                    len.ParametersValueChanged -= Length_ParametersValueChanged;
                    len.ExpressionSetting -= Length_ExpressionSetting;
                    len.ExpressionSet -= Length_ExpressionSet;
                }
                len = value;
                if (len is object)
                {
                    len.ParametersValueChanging += Length_ParametersValueChanging;
                    len.ParametersValueChanged += Length_ParametersValueChanged;
                    len.ExpressionSetting += Length_ExpressionSetting;
                    len.ExpressionSet += Length_ExpressionSet;
                }
            }
        }

        void Length_ParametersValueChanged(DocStringX sender, ParametersValueChangedEventArgs e)
        {
            if (FixedLength && StrLength.Value >= 0)
            {
                Length = StrLength.Value;
                if (tryRelocateAnotherPoint(DirectedAngle, Length))
                {

                }
                else
                {
                    Length = e.OldValue;
                }
            }
            Pad.MoveObject_DoMouseUp(Point2.Movable ? Point2 : Point1);
        }

        void Length_ParametersValueChanging(DocStringX sender)
        {
            Pad.MoveObject_DoMouseDown(Point2.Movable ? Point2 : Point1);
        }

        void Length_ExpressionSetting(StringX sender)
        {
            Pad.MoveObject_DoMouseDown(Point2.Movable ? Point2 : Point1);
        }

        [MultilingualCategory("Structure"), MultilingualDisplayName(null, "Fix", "LSLength"), TypeConverter(typeof(ArtMath.Converter.BooleanConverter)), DefaultValue(typeof(bool), "True")]
        public bool FixedLength
        {
            get { return fixLength; }
            set
            {
                fixLength = value;
                if (fixLength)
                {
                    StrLength.SetExpression(StrLength.DirectExpression);
                }
            }
        }
        [MultilingualCategory("Structure"), MultilingualDisplayName("DirectedAngle")]
        public AngleStringX StrDirectedAngle
        {
            get { return angle; }
            set
            {
                if (angle is object)
                {
                    angle.ParametersValueChanging -= DirectedAngle_ParametersValueChanging;
                    angle.ParametersValueChanged -= DirectedAngle_ParametersValueChanged;
                    angle.ExpressionSetting -= DirectedAngle_ExpressionSetting;
                    angle.ExpressionSet -= DirectedAngle_ExpressionSet;
                }
                angle = value;
                if (angle is object)
                {
                    angle.ParametersValueChanging += DirectedAngle_ParametersValueChanging;
                    angle.ParametersValueChanged += DirectedAngle_ParametersValueChanged;
                    angle.ExpressionSetting += DirectedAngle_ExpressionSetting;
                    angle.ExpressionSet += DirectedAngle_ExpressionSet;
                }
            }
        }

        void DirectedAngle_ParametersValueChanged(DocStringX sender, ParametersValueChangedEventArgs e)
        {
            if (FixedDirectedAngle)
            {
                DirectedAngle = StrDirectedAngle.RadValue;
                if (tryRelocateAnotherPoint(DirectedAngle, Length))
                {

                }
                else
                {
                    DirectedAngle = Pad.GetRad(e.OldValue);
                }
            }
            Pad.MoveObject_DoMouseUp(Point2.Movable ? Point2 : Point1);
        }

        void DirectedAngle_ParametersValueChanging(DocStringX sender)
        {
            Pad.MoveObject_DoMouseDown(Point2.Movable ? Point2 : Point1);
        }

        void DirectedAngle_ExpressionSetting(StringX sender)
        {
            Pad.MoveObject_DoMouseDown(Point2.Movable ? Point2 : Point1);
        }

        [MultilingualCategory("Structure"), MultilingualDisplayName(null, "Fix", "DirectedAngle"), TypeConverter(typeof(ArtMath.Converter.BooleanConverter)), DefaultValue(typeof(bool), "True")]
        public bool FixedDirectedAngle
        {
            get { return fixAngle; }
            set
            {
                fixAngle = value;
                if (fixAngle)
                {
                    StrDirectedAngle.SetExpression(StrDirectedAngle.DirectExpression);
                }
            }
        }
        public override int ClickTimes
        {
            get
            {
                return 1;
            }
        }
        //[Browsable(false)]
        //public double NewPDirectedAngle
        //{
        //    get
        //    {
        //        return Point1.NewPxy.GetAngle(Point2.NewPxy);
        //    }
        //}
        [MultilingualCategory("Parameter"), MultilingualDisplayName("DirectedAngle"), TypeConverter(typeof(IntelliAngleConverter))]
        public double DirectedAngle
        {
            get { return dirAngle; }
            set
            {
                dirAngle = value;
            }
        }
        public override double ArrivalAngle
        {
            get
            {
                return NewDirectedAngle;
            }
        }
        public override double NewSlope
        {
            get
            {
                if (FixedDirectedAngle)
                {
                    return Math.Tan(StrDirectedAngle.RadValue);
                }
                return base.NewSlope;
            }
        }
        [Browsable(false)]
        public double NewDirectedAngle
        {
            get
            {
                return FixedDirectedAngle ? StrDirectedAngle.RadValue : Point1.NewXY.GetAngle(Point2.NewXY);
            }
        }
        public override double NewLength
        {
            get
            {
                if (FixedLength)
                {
                    return StrLength.Value;
                }
                return base.NewLength;
            }
        }
        public override bool PointsHaveConstraint
        {
            get { return true; }
        }
        bool tryRelocateAnotherPoint(double angle, double len)
        {
            permit = true;
            bool res = false;
            if (Point2.Movable)
            {
                Point1.TempMoveLock = true;
                res = Pad.MoveObject_DoMouseMove(Point2, Pad.XYToPxy(Point1.ToPointD().Offset(angle, len)));
                Point1.TempMoveLock = false;
            }
            else if (Point1.Movable)
            {
                Point2.TempMoveLock = true;
                res = Pad.MoveObject_DoMouseMove(Point1, Pad.XYToPxy(Point2.ToPointD().BeforeOffset(angle, len)));
                Point2.TempMoveLock = false;
            }
            permit = false;
            return res;
        }
        public override void RefreshPropertyDisplayersText()
        {
            DirectedAngleDisplayer.RefreshText();
            base.RefreshPropertyDisplayersText();
        }
        public override void RefreshPropertyDisplayersLocation()
        {
            DirectedAngleDisplayer.RefreshLocation();
            base.RefreshPropertyDisplayersLocation();
        }
        public override void PaintProperties(Graphics gra)
        {
            if (ObjectState == ObjectState.Normal && Visible)
            {
                DirectedAngleDisplayer.Paint(gra);
            }
            base.PaintProperties(gra);
        }
        public override void ComponentChanged(ShapeBase component)
        {
            if (!FixedDirectedAngle)
            {
                DirectedAngle = NewDirectedAngle;
            }
            base.ComponentChanged(component);
        }
        public override PointCollectionBase[] GetInPointMovingRange(PointBase movePoint, Base sender = null)
        {
            if (!permit)
            {
                switch (CountMovablePoints())
                {
                    case 1:
                        if (FixedDirectedAngle)
                        {
                            if (FixedLength)
                            {
                                return new PointCollectionBase[] { new DiscontinuityPoint(movePoint == Point1 ? Point2.NewXY.BeforeOffset(NewDirectedAngle, NewLength) : Point1.NewXY.Offset(NewDirectedAngle, NewLength)) };
                            }
                            else
                            {
                                return new PointCollectionBase[] { new StraightLine((movePoint == Point1 ? Point2 : Point1).NewXY, NewSlope) };
                            }
                        }
                        else
                        {
                            if (FixedLength)
                            {
                                return new PointCollectionBase[] { new PointCollection.Circle((movePoint == Point1 ? Point2 : Point1).NewXY, NewLength) };
                            }
                        }
                        break;
                }
            }
            return base.GetInPointMovingRange(movePoint, sender);
        }
        public override PropertyDisplayer[] PropertyDisplayersCheckNearBy(PointD Plocation)
        {
            List<PropertyDisplayer> res = new List<PropertyDisplayer>(base.PropertyDisplayersCheckNearBy(Plocation));
            if (DirectedAngleDisplayer.InRegion(Plocation))
            {
                res.Add(DirectedAngleDisplayer);
            }
            return res.ToArray();
        }
        public override void CreateComponents()
        {
            if (Point1 == null)
            {
                CreateComponents($"{Resources.Translate("EndPoint")}1");
                CreateComponents(Pad.CreatePoint($"{Resources.Translate("EndPoint")}2", Pad.XYToPxy(Point1.ToPointD().Offset(StrDirectedAngle.RadValue, StrLength.Value)), false));//此处已调用base.CreateComponents();
            }
        }
        public override object CopyAndPaste(SketchPad pad)
        {
            DirectedConstrainedLineSegment dcls = new DirectedConstrainedLineSegment(pad, Utility.GetNextAvailableName(Name, pad.CheckObjectNameExist), StrDirectedAngle.DirectExpression, StrLength.DirectExpression, LayerItem != null);
            afterNewCopy(dcls);
            return dcls;
        }
    }
}
